package org.eli;

import java.awt.image.BufferedImage;

public class Vec3 {
    //定义最基本的数据格式：三维的浮点型坐标
    public double[] e = new double[3];

    //构造函数
    public Vec3() {
    }

    public Vec3(double x, double y, double z) {
        e[0] = x;
        e[1] = y;
        e[2] = z;
    }

    //获取三维坐标任一维度的索引，返回对应坐标的值
    public double x() {
        return e[0];
    }

    public double y() {
        return e[1];
    }

    public double z() {
        return e[2];
    }

    //向量求和
    public Vec3 add(double c) {
        return new Vec3(e[0] + c, e[1] + c, e[2] + c);
    }

    public Vec3 add(Vec3 a, Vec3 b) {
        return new Vec3(a.e[0] + b.e[0], a.e[1] + b.e[1], a.e[2] + b.e[2]);
    }

    public Vec3 add(Vec3 v) {
        return new Vec3(e[0] + v.e[0], e[1] + v.e[1], e[2] + v.e[2]);
    }

    //向量相减
    public Vec3 subtract(double c) {
        return new Vec3(e[0] - c, e[1] - c, e[2] - c);
    }
    public Vec3 subtract(Vec3 a, Vec3 b) {
        return new Vec3(a.e[0] - b.e[0], a.e[1] - b.e[1], a.e[2] - b.e[2]);
    }

    public Vec3 subtract(Vec3 v) {
        return new Vec3(e[0] - v.e[0], e[1] - v.e[1], e[2] - v.e[2]);
    }

    //向量数乘
    public Vec3 scale(Vec3 a, double c) {
        return new Vec3(a.e[0] * c, a.e[1] * c, a.e[2] * c);
    }

    public Vec3 scale(double t) {
        return new Vec3(e[0] * t, e[1] * t, e[2] * t);
    }

    //向量除
    public Vec3 divide(Vec3 a, double c) {
        return new Vec3(a.e[0] / c, a.e[1] / c, a.e[2] / c);
    }

    public Vec3 divide(double t) {
        return new Vec3(e[0] / t, e[1] / t, e[2] / t);
    }

    //矩阵相乘
    public Vec3 multiply(Vec3 a, Vec3 b) {
        return new Vec3(a.e[0] * b.e[0], a.e[1] * b.e[1], a.e[2] * b.e[2]);
    }

    public Vec3 multiply(Vec3 v) {
        return new Vec3(e[0] * v.e[0], e[1] * v.e[1], e[2] * v.e[2]);
    }

    public Vec3 negate() {
        return new Vec3(-e[0], -e[1], -e[2]);
    }

    //求向量的模长
    public double length() {
        return Math.sqrt(
                Math.pow(e[0], 2.0) + Math.pow(e[1], 2.0) + Math.pow(e[2], 2.0)
        );
    }

    //模长的平方
    public double sqrLength() {
        return Math.pow(e[0], 2.0) + Math.pow(e[1], 2.0) + Math.pow(e[2], 2.0);
    }

    //向量归一化
    public Vec3 normalize(Vec3 v) {
        return new Vec3(v.x() / v.length(), v.y() / v.length(), v.z() / v.length());
    }

    public Vec3 normalize() {
        return new Vec3(e[0] / length(), e[1] / length(), e[2] / length());
    }

    //点乘
    public double dot(Vec3 a, Vec3 b) {
        return (a.x() * b.x() + a.y() * b.y() + a.z() * b.z());
    }

    public double dot(Vec3 v) {
        return (e[0] * v.e[0] + e[1] * v.e[1] + e[2] * v.e[2]);
    }

    //叉乘
    public Vec3 cross(Vec3 a, Vec3 b) {
        return new Vec3(a.y() * b.z() - a.z() * b.y(),
                a.z() * b.x() - a.x() * b.z(),
                a.x() * b.y() - a.y() * b.x());
    }

    public Vec3 cross(Vec3 v) {
        return new Vec3(e[1] * v.z() - e[2] * v.y(),
                e[2] * v.x() - e[0] * v.z(),
                e[0] * v.y() - e[1] * v.x());
    }

    // 上色
    public void writeColor(BufferedImage image, int i, int j, int samplesPerPixel, int imageHeight) {
        double scale = 1.0 / samplesPerPixel;
        double r = Math.sqrt(scale * e[0]);
        double g = Math.sqrt(scale * e[1]);
        double b = Math.sqrt(scale * e[2]);

        int ir = (int) (256 * RtWeekend.clamp(r, 0.0, 0.999));
        int ig = (int) (256 * RtWeekend.clamp(g, 0.0, 0.999));
        int ib = (int) (256 * RtWeekend.clamp(b, 0.0, 0.999));
        int color = (ir << 16) | (ig << 8) | ib; // 将颜色组件合并为一个整数
        image.setRGB(i, imageHeight - 1 - j, color); // 注意图像的y坐标是倒置的
    }

    // 随机生成向量
    public static Vec3 random() {
        return new Vec3(RtWeekend.randomDouble(), RtWeekend.randomDouble(), RtWeekend.randomDouble());
    }

    public static Vec3 random(double min, double max) {
        return new Vec3(RtWeekend.randomDouble(min, max), RtWeekend.randomDouble(min, max), RtWeekend.randomDouble(min, max));
    }

    public static Vec3 randomInUnitSphere() {
        while (true) {
            Vec3 p = random(-1, 1);
            if (p.sqrLength() >= 1) continue;
            return p;
        }
    }

    public static Vec3 randomUnitVector() {
        double a = RtWeekend.randomDouble(0, 2 * Math.PI); // [0, 2*pi)
        double z = RtWeekend.randomDouble(-1, 1); // [-1, 1)
        double r = Math.sqrt(1 - z * z);
        return new Vec3(r * Math.cos(a), r * Math.sin(a), z);
    }

    public static Vec3 randomInHemisphere(Vec3 normal) {
        Vec3 inUnitSphere = randomInUnitSphere();

        if (inUnitSphere.dot(normal) > 0.0) //
            return inUnitSphere;
        else
            return inUnitSphere.negate();
    }

    public static Vec3 reflect(Vec3 v, Vec3 n) {

        return v.subtract(n.scale(2 * v.dot(n)));
    }

    public static Vec3 reflect(Vec3 v, Vec3 n, double etaiOverEtat) {
        double dot = n.dot(v.negate());
        Vec3 rOutParallel = (v.add(n.scale(dot))).scale(etaiOverEtat);
        Vec3 rOutPerp = n.scale(-Math.sqrt(1.0 - rOutParallel.sqrLength()));
        return rOutParallel.add(rOutPerp);
    }

    public static Vec3 randomInUnitDisk() {
        while (true) {
            Vec3 vec3 = new Vec3(RtWeekend.randomDouble(-1, 1), RtWeekend.randomDouble(-1, 1), 0);
            if (vec3.sqrLength() >= 1) {
                continue;
            }
            return vec3;
        }
    }


}
